package top.ply.userservice.service.impl;

import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import top.ply.authservice.pojo.CommonUser;
import top.ply.authservice.service.CommonAuthService;
import top.ply.common_unit.entity.RespEntity;
import top.ply.common_unit.global_resp.RespUtil;
import top.ply.message.service.EmailMsgService;
import top.ply.message.service.TelephoneMsgService;
import top.ply.userservice.dao.CommonUserMapper;
import top.ply.userservice.service.CommonUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.*;

@Service
public class CommonUserServiceImpl implements CommonUserService {

    @DubboReference(interfaceClass = CommonAuthService.class)
    CommonAuthService commonAuthService;

    @Autowired
    CommonUserMapper commonUserMapper;

    @Resource(name = "phoneCodeRedisTemplate")
    private StringRedisTemplate phoneCodeRedisTemplate;

    @Resource(name = "emailCodeRedisTemplate")
    private StringRedisTemplate emailCodeRedisTemplate;

    @Resource(name = "updatePhoneRedisTemplate")
    private StringRedisTemplate updatePhoneRedisTemplate;

    @Resource(name = "updateEmailRedisTemplate")
    private StringRedisTemplate updateEmailRedisTemplate;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Value("${spring.rabbitmq.verify-code-exchange.name}")
    public String verifyCodeExchangeName;

    @Value("${spring.rabbitmq.email-verify-code-queue.routingKey}")
    public String emailVerifyCodeQueueRoutingKey;

    @Value("${spring.rabbitmq.phone-verify-code-queue.routingKey}")
    public String phoneVerifyCodeQueueRoutingKey;

    public static final Integer codeExpireTime = 5;
    public static final TimeUnit codeExpireTimeUnit = TimeUnit.MINUTES;

    @Override
    @Transactional
    public RespEntity bindPassword(String token, String password) {

        boolean b = commonAuthService.verifyToken(token);
        if (!b) {
            return RespUtil.tokenVerifyError();
        }

        if (!StringUtils.hasText(password)) {
            return RespUtil.illegalParameter();
        }

        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        if (savedUser == null) {
            return RespUtil.tokenVerifyError();
        }

        String savedPassword = savedUser.getPassword();
        if (StringUtils.hasText(savedPassword)) {
            return RespUtil.illegalParameter();
        }

        String encryptPass = DigestUtils.md5DigestAsHex(password.getBytes());
        savedUser.setPassword(encryptPass);
        int update = commonUserMapper.updateCommonUser(savedUser);
        return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
    }



    @Override
    @Transactional
    public RespEntity bindUsername(String token, String username) {

        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }

        if (!StringUtils.hasText(username)) {
            return RespUtil.illegalParameter();
        }

        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser commonUserByUsername = commonUserMapper.getCommonUserByUsername(username);
        if (commonUserByUsername != null) {
            return RespUtil.conflictError();
        }


        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        String savedUsername = savedUser.getUsername();
        if (StringUtils.hasText(savedUsername)) {
            return RespUtil.illegalParameter();
        }

        savedUser.setUsername(username);
        int update = commonUserMapper.updateCommonUser(savedUser);
        return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
    }

    @Override
    public RespEntity sendBindTelephoneVerifyCode(String token, String telephone) {

        if (!CommonAuthService.PHONE_PATTERN.matcher(telephone).matches()) {
            return RespUtil.illegalParameter();
        }

        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }
        String userID = commonAuthService.getUserIDFromToken(token);
        if (userID == null) {
            return RespUtil.tokenVerifyError();
        }
        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        if (savedUser == null) {
            return RespUtil.tokenVerifyError();
        }
        if (StringUtils.hasText(savedUser.getTelephone())) {
            return RespUtil.illegalParameter();
        }

        CommonUser commonUserByTelephone = commonUserMapper.getCommonUserByTelephone(telephone);
        if (commonUserByTelephone != null) {
            return RespUtil.conflictError();
        }

        String verifyCode = generateVerifyCode();

        SetOperations<String, String> ops = phoneCodeRedisTemplate.opsForSet();
        Long add = ops.add(telephone, verifyCode);
        phoneCodeRedisTemplate.expire(telephone, codeExpireTime, codeExpireTimeUnit);

        sendPhoneVerifyCode(telephone, verifyCode);

        return add > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();

    }

    @Override
    @Transactional
    public RespEntity doBindTelephoneWithVerifyCode(String token, String telephone, String code) {
        if (!StringUtils.hasText(telephone)) {
            return RespUtil.illegalParameter();
        }
        if (!CommonAuthService.PHONE_PATTERN.matcher(telephone).matches()) {
            return RespUtil.illegalParameter();
        }
        if (!StringUtils.hasText(token)) {
            return RespUtil.illegalParameter();
        }
        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser commonUserByTelephone = commonUserMapper.getCommonUserByTelephone(telephone);
        if (commonUserByTelephone != null) {
            return RespUtil.conflictError();
        }

        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        if (savedUser == null) {
            return RespUtil.tokenVerifyError();
        }

        SetOperations<String, String> ops = phoneCodeRedisTemplate.opsForSet();
        if (!ops.isMember(telephone, code)) {
            return RespUtil.verifyCodeError();
        } else {
            savedUser.setTelephone(telephone);
            int update = commonUserMapper.updateCommonUser(savedUser);
            return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
        }
    }

    @Override
    public RespEntity sendBindEmailVerifyCode(String token, String email) {
        if (!CommonAuthService.EMAIL_PATTERN.matcher(email).matches()) {
            return RespUtil.illegalParameter();
        }
        if ((!StringUtils.hasText(token)) || (!StringUtils.hasText(email))) {
            return RespUtil.illegalParameter();
        }
        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser commonUserByEmail = commonUserMapper.getCommonUserByEmail(email);
        if (commonUserByEmail != null) {
            return RespUtil.conflictError();
        }

        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        if (StringUtils.hasText(savedUser.getEmail())) {
            return RespUtil.illegalParameter();
        }

        String verifyCode = generateVerifyCode();
        SetOperations<String, String> ops = emailCodeRedisTemplate.opsForSet();
        Long add = ops.add(email, verifyCode);
        emailCodeRedisTemplate.expire(email, codeExpireTime, codeExpireTimeUnit);
        sendEmailVerifyCode(email, verifyCode);
        return add > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
    }

    @Override
    @Transactional
    public RespEntity doBindEmailWithVerifyCode(String token, String email, String code) {

        if ((!StringUtils.hasText(token)) || (!StringUtils.hasText(email) || (!StringUtils.hasText(code)))) {
            return RespUtil.illegalParameter();
        }

        if (!CommonAuthService.EMAIL_PATTERN.matcher(email).matches()) {
            return RespUtil.illegalParameter();
        }

        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }

        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser commonUserByEmail = commonUserMapper.getCommonUserByEmail(email);
        if (commonUserByEmail != null) {
            return RespUtil.conflictError();
        }

        CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
        if (savedUser == null) {
            return RespUtil.tokenVerifyError();
        }

        SetOperations<String, String> ops = emailCodeRedisTemplate.opsForSet();
        if (!ops.isMember(email, code)) {
            return RespUtil.verifyCodeError();
        } else {
            savedUser.setEmail(email);
            int update = commonUserMapper.updateCommonUser(savedUser);
            return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
        }
    }

    @Override
    @Transactional
    public RespEntity updatePassword(String token, String oldPassword, String newPassword) {

        if ((!StringUtils.hasText(token)) || (!StringUtils.hasText(oldPassword)) || (!StringUtils.hasText(newPassword))) {
            return RespUtil.illegalParameter();
        }

        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }

        String userID = commonAuthService.getUserIDFromToken(token);
        if (userID == null) {
            return RespUtil.illegalParameter();
        }

        CommonUser savedCommonUser = commonUserMapper.getCommonUserByID(userID);

        if (savedCommonUser == null) {
            return RespUtil.illegalParameter();
        }

        String savedPassword = savedCommonUser.getPassword();
        if (!StringUtils.hasText(savedPassword)) {
            return RespUtil.baseFail();
        }

        String encryptDeliverOldPass = DigestUtils.md5DigestAsHex(oldPassword.getBytes());
        if (!encryptDeliverOldPass.equals(savedPassword)) {
            return RespUtil.userPasswordError();
        } else {
            String encryptNewPass = DigestUtils.md5DigestAsHex(newPassword.getBytes());
            synchronized (this) {
                savedCommonUser.setPassword(encryptNewPass);
                commonUserMapper.updateCommonUser(savedCommonUser);
            }
            return RespUtil.baseSuccess();
        }

    }

    @Override
    @Transactional
    public RespEntity updateUsername(String token, String username) {
        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser commonUserByUsername = commonUserMapper.getCommonUserByUsername(username);
        String userID = commonAuthService.getUserIDFromToken(token);

        if (commonUserByUsername != null) {
            return commonUserByUsername.getUserID().equals(userID) ? RespUtil.baseSuccess() : RespUtil.conflictError();
        } else {
            CommonUser savedUser = commonUserMapper.getCommonUserByID(userID);
            savedUser.setUsername(username);
            int update = commonUserMapper.updateCommonUser(savedUser);
            return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
        }
    }

    @Override
    public RespEntity sendUpdateTelephoneVerifyCode(String token) {
        if (!commonAuthService.verifyToken(token)) {
            return RespUtil.tokenVerifyError();
        }
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }
        CommonUser user = commonUserMapper.getCommonUserByID(userID);
        if (user == null) {
            return RespUtil.illegalParameter();
        }
        String telephone = user.getTelephone();
        if (!StringUtils.hasText(telephone)) {
            return RespUtil.userStatusError();
        } else {
            String verifyCode = generateVerifyCode();
            SetOperations<String, String> ops = updatePhoneRedisTemplate.opsForSet();
            Long add = ops.add(telephone, verifyCode);
            updatePhoneRedisTemplate.expire(telephone, codeExpireTime, codeExpireTimeUnit);
            if (add > 0) {
                sendPhoneVerifyCode(telephone, verifyCode);
                return RespUtil.baseSuccess();
            } else {
                return RespUtil.baseFail();
            }
        }
    }

    @Override
    @Transactional
    public RespEntity doUpdateTelephoneWithVerifyCode(String token, String code, String newPhone) {
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }

        CommonUser user = commonUserMapper.getCommonUserByID(userID);
        if (user == null) {
            return RespUtil.tokenVerifyError();
        }
        String telephone = user.getTelephone();
        if (telephone == null) {
            return RespUtil.illegalParameter();
        }
        SetOperations<String, String> ops = updatePhoneRedisTemplate.opsForSet();
        if (!ops.isMember(telephone, code)) {
            return RespUtil.verifyCodeError();
        } else {
            user.setTelephone(newPhone);
            int update = commonUserMapper.updateCommonUser(user);
            return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
        }
    }

    @Override
    public RespEntity sendUpdateEmailVerifyCode(String token) {
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }
        CommonUser user = commonUserMapper.getCommonUserByID(userID);
        if (user == null) {
            return RespUtil.illegalParameter();
        }
        String email = user.getEmail();
        if (!StringUtils.hasText(email)) {
            return RespUtil.userStatusError();
        }
        String verifyCode = generateVerifyCode();
        SetOperations<String, String> ops = updateEmailRedisTemplate.opsForSet();
        Long add = ops.add(email, verifyCode);
        updateEmailRedisTemplate.expire(email, codeExpireTime, codeExpireTimeUnit);
        if (add > 0) {

            sendEmailVerifyCode(email, verifyCode);

            return RespUtil.baseSuccess();
        } else {
            return RespUtil.baseFail();
        }
    }

    @Override
    @Transactional
    public RespEntity doUpdateEmailWithVerifyCode(String token, String code, String newMail) {
        String userID = commonAuthService.getUserIDFromToken(token);
        if (!StringUtils.hasText(userID)) {
            return RespUtil.tokenVerifyError();
        }
        CommonUser user = commonUserMapper.getCommonUserByID(userID);
        if (user == null) {
            return RespUtil.illegalParameter();
        }

        String email = user.getEmail();
        if (!StringUtils.hasText(email)) {
            return RespUtil.userStatusError();
        }
        SetOperations<String, String> ops = updateEmailRedisTemplate.opsForSet();
        if (!ops.isMember(email, code)) {
            return RespUtil.verifyCodeError();
        } else {
            user.setEmail(newMail);
            int update = commonUserMapper.updateCommonUser(user);
            return update > 0 ? RespUtil.baseSuccess() : RespUtil.baseFail();
        }
    }

    public String generateVerifyCode() {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            int j = random.nextInt(10);
            sb.append(j);
        }
        return sb.toString();
    }

    private void sendEmailVerifyCode(String email, String verifyCode) {
        new Thread(() -> {
            Map<String, String> mailAndCode = new HashMap<>();
            mailAndCode.put(EmailMsgService.sendToKey, email);
            mailAndCode.put(EmailMsgService.verifyCodeKey, verifyCode);
            Message<Map<String, String>> message = MessageBuilder.withPayload(mailAndCode).build();
            rabbitTemplate.convertAndSend(verifyCodeExchangeName, emailVerifyCodeQueueRoutingKey, message);
        }).start();
    }

    private void sendPhoneVerifyCode(String telephone, String verifyCode) {
        new Thread(() -> {
            Map<String, String> phoneAndCode = new HashMap<>();
            phoneAndCode.put(TelephoneMsgService.sendToKey, telephone);
            phoneAndCode.put(TelephoneMsgService.VERIFY_CODE_KEY, verifyCode);
            Message<Map<String, String>> message = MessageBuilder.withPayload(phoneAndCode).build();
            rabbitTemplate.convertAndSend(verifyCodeExchangeName, phoneVerifyCodeQueueRoutingKey, message);
        }).start();
    }
}
